package com.mnsq.common.tools.jpa;

import com.mnsq.common.bean.annotation.JpaFields;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Administrator
 */
@Slf4j
public class JpaExample<T, V> {

    private JpaExample() {
    }

    public static JpaExample getInstance() {
        return new JpaExample();
    }

    /**
     * 查询对象组装获取
     *
     * @param condition
     * @param obj
     * @return
     */
    public Example getExample(T condition, V obj) {
        if (obj == null) {
            throw new IllegalArgumentException("必要参数【查询对象】为空，查询失败");
        }
        List<Query> list = convertQuery(condition, obj);
        ExampleMatcher matching = ExampleMatcher.matching();
        for (Query qr : list) {
            if (JpaFields.AttrType.EQ.equals(qr.getAttrType())) {
                matching = matching.withMatcher(qr.getFieldName(), ExampleMatcher.GenericPropertyMatchers.exact().ignoreCase(qr.isIgnoreCase()));
            } else if (JpaFields.AttrType.LK.equals(qr.attrType)) {
                matching = matching.withMatcher(qr.getFieldName(), ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase(qr.isIgnoreCase()));
            } else if (JpaFields.AttrType.STW.equals(qr.attrType)) {
                matching = matching.withMatcher(qr.getFieldName(), ExampleMatcher.GenericPropertyMatchers.startsWith().ignoreCase(qr.isIgnoreCase()));
            } else if (JpaFields.AttrType.EDW.equals(qr.attrType)) {
                matching = matching.withMatcher(qr.getFieldName(), ExampleMatcher.GenericPropertyMatchers.endsWith().ignoreCase(qr.isIgnoreCase()));
            }
        }
        return Example.of(obj, matching);
    }

    /**
     * 转换条件对象-->>>内部查询对象集合，并补充Example（obj）对象字段值
     *
     * @param condition 条件对象，内置查询条件
     * @param obj       Example内模板对象
     * @return
     */
    private List<Query> convertQuery(T condition, V obj) {
        List<Query> list = new ArrayList<>();
        // 获取所有字段
        List<Field> conditionFields = getFields(condition.getClass());
        Query query;
        Field declaredField;
        String name = "";
        for (Field fd : conditionFields) {
            try {
                fd.setAccessible(true);
                // 查询条件为空时，不做处理
                if (fd.get(condition) == null || StringUtils.isBlank(fd.get(condition).toString()) || "serialVersionUID".equals(fd.getName())) {
                    continue;
                }
                Class<?> type = fd.getType();
                name = fd.getName();
                JpaFields.AttrType at = getType(type);
                JpaFields annotation = fd.getAnnotation(JpaFields.class);
                if (annotation != null) {
                    if (annotation.ignore()) {
                        continue;
                    }
                    // 若注解内指定查询方式，则使用指定的方式
                    if (annotation.attrType() != null) {
                        at = annotation.attrType();
                    }
                    // 若注解内指定字段名称，则使用指定字段名称
                    if (StringUtils.isNotBlank(annotation.fieldName())) {
                        name = annotation.fieldName();
                    }
                }
                declaredField = getField(obj.getClass(), name);
                if (declaredField == null) {
                    continue;
                }
                declaredField.setAccessible(true);
                declaredField.set(obj, fd.get(condition));
                query = new Query(name, type, annotation == null ? false : annotation.ignoreCase(), at);
                list.add(query);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                System.out.println("IllegalAccessException, error property :" + name);
                continue;
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Exception, error property :" + name);
                continue;
            }
        }
        return list;
    }

    /**
     * 默认：整形和long精确查询，其他为模糊查询
     *
     * @param type
     * @return
     */
    private JpaFields.AttrType getType(Class type) {
        if (type == Integer.class
                || type == Long.class) {
            return JpaFields.AttrType.EQ;
        }
        if (type == Enum.class) {
            return JpaFields.AttrType.EQ;
        }
        // 默认模糊查询
        return JpaFields.AttrType.LK;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Query {
        private String fieldName;
        private Class fieldType;
        private boolean ignoreCase;
        private JpaFields.AttrType attrType;
    }

    public static Field getField(Class v, String name) {
        for (Field fd : getFields(v)) {
            if (fd.getName().equalsIgnoreCase(name)) {
                return fd;
            }
        }
        return null;
    }

    private static List<Field> getFields(Class t) {
        List<Field> fields = new ArrayList<>();
        for (Class<?> c = t; c != Object.class; c = c.getSuperclass()) {
            fields.addAll(Arrays.asList(c.getDeclaredFields()));
        }
        return fields;
    }
}
